using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using ICodeShare.UI.Converters.Extensions;

namespace ICodeShare.UI.Converters.Helpers
{
	public class ExceptionHelper
	{
		private struct ExceptionInfoKey : IEquatable<ExceptionHelper.ExceptionInfoKey>
		{
			private readonly Assembly assembly;
			private readonly string resourceName;
			public ExceptionInfoKey(Assembly assembly, string resourceName)
			{
				this.assembly = assembly;
				this.resourceName = resourceName;
			}
			public bool Equals(ExceptionHelper.ExceptionInfoKey other)
			{
				return other.assembly.Equals(this.assembly) && string.Equals(other.resourceName, this.resourceName, StringComparison.Ordinal);
			}
			public override bool Equals(object obj)
			{
				return obj is ExceptionHelper.ExceptionInfoKey && this.Equals((ExceptionHelper.ExceptionInfoKey)obj);
			}
			public override int GetHashCode()
			{
				int num = 17;
				num = num * 23 + this.assembly.GetHashCode();
				return num * 23 + this.resourceName.GetHashCode();
			}
		}
		private const string typeAttributeName = "type";
		private static readonly IDictionary<ExceptionHelper.ExceptionInfoKey, XDocument> exceptionInfos = new Dictionary<ExceptionHelper.ExceptionInfoKey, XDocument>();
		private static readonly object exceptionInfosLock = new object();
		private readonly Type forType;
		private readonly string resourceName;
		public ExceptionHelper(Type forType) : this(forType, null, 0)
		{
		}
		public ExceptionHelper(Type forType, string resourceName) : this(forType, resourceName, 0)
		{
			resourceName.AssertNotNullOrEmpty("resource", true);
		}
		private ExceptionHelper(Type forType, string resourceName, int dummy)
		{
			forType.AssertNotNull("forType");
			this.forType = forType;
			if (resourceName != null)
			{
				this.resourceName = resourceName;
				return;
			}
			this.resourceName = forType.Assembly.GetName().Name + ".Properties.ExceptionHelper.xml";
		}
		[DebuggerHidden]
		public Exception Resolve(string exceptionKey, params object[] messageArgs)
		{
			return this.Resolve(exceptionKey, null, null, messageArgs);
		}
		[DebuggerHidden]
		public Exception Resolve(string exceptionKey, Exception innerException, params object[] messageArgs)
		{
			return this.Resolve(exceptionKey, null, innerException, messageArgs);
		}
		[DebuggerHidden]
		public Exception Resolve(string exceptionKey, object[] constructorArgs, Exception innerException)
		{
			return this.Resolve(exceptionKey, constructorArgs, innerException, null);
		}
		[DebuggerHidden]
		public Exception Resolve(string exceptionKey, object[] constructorArgs, params object[] messageArgs)
		{
			return this.Resolve(exceptionKey, constructorArgs, null, messageArgs);
		}
		[DebuggerHidden]
		public Exception Resolve(string exceptionKey, object[] constructorArgs, Exception innerException, params object[] messageArgs)
		{
			exceptionKey.AssertNotNull("exceptionKey");
			XDocument exceptionInfo = ExceptionHelper.GetExceptionInfo(this.forType.Assembly, this.resourceName);
			XElement xElement = (
				from exceptionGroup in exceptionInfo.Element("exceptionHelper").Elements("exceptionGroup")
				from exception in exceptionGroup.Elements("exception")
				where string.Equals(exceptionGroup.Attribute("type").Value, this.forType.FullName, StringComparison.Ordinal) && string.Equals(exception.Attribute("key").Value, exceptionKey, StringComparison.Ordinal)
				select exception).FirstOrDefault<XElement>();
			if (xElement == null)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The exception details for key '{0}' could not be found at /exceptionHelper/exceptionGroup[@type'{1}']/exception[@key='{2}'].", new object[]
				{
					exceptionKey,
					this.forType,
					exceptionKey
				}));
			}
			XAttribute xAttribute = xElement.Attribute("type");
			if (xAttribute == null)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The '{0}' attribute could not be found for exception with key '{1}'", new object[]
				{
					"type",
					exceptionKey
				}));
			}
			Type type = Type.GetType(xAttribute.Value);
			if (type == null)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type '{0}' could not be loaded for exception with key '{1}'", new object[]
				{
					xAttribute.Value,
					exceptionKey
				}));
			}
			if (!typeof(Exception).IsAssignableFrom(type))
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type '{0}' for exception with key '{1}' does not inherit from '{2}'", new object[]
				{
					type.FullName,
					exceptionKey,
					typeof(Exception).FullName
				}));
			}
			string text = xElement.Value.Trim();
			if (messageArgs != null && messageArgs.Length > 0)
			{
				text = string.Format(CultureInfo.InvariantCulture, text, messageArgs);
			}
			List<object> list = new List<object>();
			list.Add(text);
			if (constructorArgs != null)
			{
				list.AddRange(constructorArgs);
			}
			if (innerException != null)
			{
				list.Add(innerException);
			}
			object[] parameters = list.ToArray();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public;
			ConstructorInfo constructorInfo = null;
			try
			{
				object obj;
				constructorInfo = (ConstructorInfo)Type.DefaultBinder.BindToMethod(bindingAttr, type.GetConstructors(bindingAttr), ref parameters, null, null, null, out obj);
			}
			catch (MissingMethodException)
			{
			}
			if (constructorInfo == null)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "An appropriate constructor could not be found for exception type '{0}, for exception with key '{1}'", new object[]
				{
					type.FullName,
					exceptionKey
				}));
			}
			return (Exception)constructorInfo.Invoke(parameters);
		}
		[DebuggerHidden]
		public void ResolveAndThrowIf(bool condition, string exceptionKey, params object[] messageArgs)
		{
			if (condition)
			{
				throw this.Resolve(exceptionKey, messageArgs);
			}
		}
		[DebuggerHidden]
		public void ResolveAndThrowIf(bool condition, string exceptionKey, Exception innerException, params object[] messageArgs)
		{
			if (condition)
			{
				throw this.Resolve(exceptionKey, innerException, messageArgs);
			}
		}
		[DebuggerHidden]
		public void ResolveAndThrowIf(bool condition, string exceptionKey, object[] constructorArgs, Exception innerException)
		{
			if (condition)
			{
				throw this.Resolve(exceptionKey, constructorArgs, innerException);
			}
		}
		[DebuggerHidden]
		public void ResolveAndThrowIf(bool condition, string exceptionKey, object[] constructorArgs, params object[] messageArgs)
		{
			if (condition)
			{
				throw this.Resolve(exceptionKey, constructorArgs, messageArgs);
			}
		}
		[DebuggerHidden]
		public void ResolveAndThrowIf(bool condition, string exceptionKey, object[] constructorArgs, Exception innerException, params object[] messageArgs)
		{
			if (condition)
			{
				throw this.Resolve(exceptionKey, constructorArgs, innerException, messageArgs);
			}
		}
		[DebuggerHidden]
		private static XDocument GetExceptionInfo(Assembly assembly, string resourceName)
		{
			XDocument xDocument = null;
			ExceptionHelper.ExceptionInfoKey key = new ExceptionHelper.ExceptionInfoKey(assembly, resourceName);
			lock (ExceptionHelper.exceptionInfosLock)
			{
				if (ExceptionHelper.exceptionInfos.ContainsKey(key))
				{
					xDocument = ExceptionHelper.exceptionInfos[key];
				}
				else
				{
					Stream manifestResourceStream = assembly.GetManifestResourceStream(resourceName);
					if (manifestResourceStream == null)
					{
						throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "XML resource file '{0}' could not be found in assembly '{1}'.", new object[]
						{
							resourceName,
							assembly.FullName
						}));
					}
					using (StreamReader streamReader = new StreamReader(manifestResourceStream))
					{
						xDocument = XDocument.Load(streamReader);
					}
					ExceptionHelper.exceptionInfos[key] = xDocument;
				}
			}
			return xDocument;
		}
	}
}
